import xml.dom.minidom 
from xml.dom.minidom import Node 
import Config , Event, User
from Log import *
from Utils import *
import imp

class BlinkRoom : 
    commonRoom = None               #first place that user will be their, after connected.

    def __init__( self, name, parent, creator = "@BlinkServer", canCreateSub=True, destroyOnEmpty=False, maxusers=0, plugin = None) : 
        self.name = name
        self.creator = creator
        self.parentRoom = parent
        self.subRooms = {}
        self.users  = {}
        self.events = Event.EventDispatcher(self)
        
        self.hasSub = canCreateSub
        self.destroyOnEmpty = destroyOnEmpty
        self.maxusers = maxusers
        self.notifyMessage = True # send notify to other room's member if someone moving in or out
        
        self.plugins = {}
        
        if parent :
             dbg("Creating "+name+' Son of '+  parent.name  + ' has sub '+str(canCreateSub)+str(maxusers))
        else :
             dbg("Creating "+name+' Son of None has sub '+str(canCreateSub)+str(maxusers))
             
        if (plugin!= None) and (plugin!="") :
            self.loadPlugin(plugin)
             
    def _removeUser( self, name ) :
        #print "removing ",name
        if self.users.has_key(name) : 
            #print "yes, removing ",name
            u = self.users.pop(name)
            u.events.removeListener( "xml.room", self._nodeFromUser  )
            
            if self.notifyMessage :
                u = Element("user")
                u.setAttribute("name", name)
                ele = Element("leave")
                ele.appendChild( u )
                r = Element("room")
                r.appendChild(ele)
                resp = newxml(  )
                resp.firstChild.appendChild( r )
                self.msgToAll( resp , [])
            
            self.events.dispatch('removeuser', Event.Event( {'user': u} ) )
            return u 
        if (len(self.users)<=0) and (self.destroyOnEmpty) :
            self.parent.destroySubRoom( self.name )
    
    def _addUser( self, user ) : 
        if not self.users.has_key(user.getUsername()) : 
            self.users[user.getUsername()] = user

        user.room = self;
        user.events.addListener( "xml.room", self._nodeFromUser  )
        
        if self.notifyMessage :
            ele = Element("join")
            ele.appendChild( user.getXmlInfo() )
            r = Element("room")
            r.appendChild(ele)
            resp = newxml( )
            resp.firstChild.appendChild( r )
            self.msgToAll( resp , [])
        
        self.events.dispatch('adduser', Event.Event( {'user': user} ) )
        
    #def nodeFromUser(self, user, roomNode) :
    def _nodeFromUser(self, evt) :
        roomNode = evt.args['xmlNode']
        user = evt.owner
        for node in roomNode.childNodes :
            if node.nodeName == 'info' :
                self._onGetInfo( node , user )
            if node.nodeName == 'new' :
                self._onCreateSub( node , user)
            if node.nodeName == 'move' :
                self._onMoveRoom( node , user)
    
               
    def _onGetInfo(self, node, fromUser) :
        detail = isTrue( node.getAttribute('detail') )
        resp = newxml(  )
        r = Element("room")
        r.appendChild( self.getXmlInfo( detail ) )
        resp.firstChild.appendChild( r )
        self.msgTo( fromUser.getUsername() , resp )
        


    def _onMoveRoom(self, node, fromUser) :
        path = node.getAttribute( 'to' ) 
        if path!="" : 
            r = self.getRoomFromPath( path )
            if r!= None :
                if (r.maxusers==0) or (len(r.users)<r.maxusers ):
                    fromUser.changeRoom( r )
                else :
                    ele = Element("error")
                    ele.setAttribute("error", "room full.")
                    ele.setAttribute("command", "move")
                    r = Element("room")
                    r.appendChild(ele)
                    resp = newxml( )
                    resp.firstChild.appendChild( r )
                    self.msgTo( fromUser.getUsername(), resp )
            else :
                ele = Element("error")
                ele.setAttribute("error", "room not found.")
                ele.setAttribute("command", "move")
                r = Element("room")
                r.appendChild(ele)
                resp = newxml( )
                resp.firstChild.appendChild( r )
                self.msgTo( fromUser.getUsername(), resp )

        else :
            ele = Element("error")
            ele.setAttribute("error", "attribute 'to' is required.")
            ele.setAttribute("command", "move")
            r = Element("room")
            r.appendChild(ele)
            resp = newxml( )
            resp.firstChild.appendChild( r )
            self.msgTo( fromUser.getUsername(), resp )
        
        
    def _onCreateSub(self, node, fromUser) :
            rname = node.getAttribute( "name" )
            hassub = node.getAttribute( 'hassub')
            if hassub=='' : hassub = True 
            else: hassub = isTrue(hassub)
            destroyonempty = node.getAttribute( 'destroyonempty') 
            if destroyonempty=='' : destroyonempty = True 
            else: destroyonempty = isTrue(destroyonempty)
            maxusers = node.getAttribute( 'maxusers')
            if maxusers=='' : maxusers = 0 
            else: maxusers = int(maxusers)
            
            plugin = None

            res , room = self.createSubRoom( name=rname ,userCreator = fromUser, canCreateSub=hassub, destroyOnEmpty=destroyonempty,maxusers=maxusers )
            if room==None :
                ele = Element("error")
                ele.setAttribute("error", res)
                ele.setAttribute("command", "new")
                r = Element("room")
                r.appendChild(ele)
                resp = newxml( )
                resp.firstChild.appendChild( r )
                self.msgTo( fromUser.getUsername(), resp )
            self.getRootRoom().traceRoom(fromUser) ##########################3
        
    def traceRoom (self ,user,page = '') :
        #return
        if self.name==user.getUsername() :
            print page , '*'+self.name  
        else : 
            print page , self.name  
            
        for r in self.subRooms : 
            self.subRooms[r].traceRoom(user,page+'    ')

    
    def createSubRoom(self, name, userCreator,canCreateSub=True, destroyOnEmpty=False , maxusers = 0, plugin="") :
        if  not self.hasSub:
            return "SubRoom is not allow here.", None

        if self.subRooms.has_key(name) : 
            return "Duplicate room name.", None
            
        r = None
        if userCreator : 
            r = BlinkRoom(name=name, parent=self, creator=userCreator.getUsername(),canCreateSub=canCreateSub,destroyOnEmpty=destroyOnEmpty,maxusers=maxusers,plugin=plugin)
            userCreator.changeRoom( r )
        else :
            r = BlinkRoom(name=name, parent=self, creator="@BlinkServer",canCreateSub=canCreateSub,destroyOnEmpty=destroyOnEmpty,maxusers=maxusers,plugin=plugin)

        self.subRooms[ name ] = r
        self.events.dispatch('createsub', Event.Event( {'room': r} ) )
        
        ele = Element("new")
        ele.appendChild( r.getXmlInfo( False ) )
        r = Element("room")
        r.appendChild(ele)
        resp = newxml( )
        resp.firstChild.appendChild( r )
        self.msgToAll( resp , [])

        return "ok" ,  r 
    
    def unload(self) :
        self.events.dispatch('destroy', Event.Event() )
    
    def destroySubRoom(self, name) :
        if self.subRooms.has_key(name) :
            if len(self.subRooms[name].users) >0 :
                return False,"room not empty."
            
            self.events.dispatch('removesub', Event.Event( {'room': self.subRooms[name]} ) )
            self.subRooms[name].unload()
            del self.subRooms[name]
            
            rx = Element("room")
            rx.setAttribute("name", name)
            ele = Element("close")
            ele.appendChild(rx)
            r = Element("room")
            r.appendChild(ele)
            resp = newxml( )
            resp.firstChild.appendChild( r )
            self.msgToAll( resp , [])
            return True, "ok"
                        
        return False,"room not found."
    
    def getXmlInfo(self, details=False) : 
        roomElm = Element("room")
        roomElm.setAttribute( "name", self.name);
        roomElm.setAttribute( "canCreateSub", str(self.hasSub).lower());
        roomElm.setAttribute( "users", str(len(self.users)) );
        roomElm.setAttribute( "subs", str(len(self.subRooms)) );
        roomElm.setAttribute( "maxusers", str(self.maxusers));
        
        if details :
            usersElm = Element("users")
            for u in self.users : 
                usersElm.appendChild( self.users[u].getXmlInfo() )
                
            subsElm = Element("subs")
            for r in self.subRooms : 
                subsElm.appendChild( self.subRooms[r].getXmlInfo( False ) )
                
            roomElm.appendChild(usersElm)
            roomElm.appendChild(subsElm)
        
        return roomElm
        
    def msgTo(self, uname, msg ) :
        dbg("send to "+uname+" msg:"+msg.toxml())
        if self.users.has_key(uname) : 
            self.users[uname].sendXml(msg)
    
    def msgToAll(self, msg, exceptList = []) :
        dbg("send to all msg:"+msg.toxml())
        for u in [ux for ux in self.users if not ux in exceptList ] :
            self.msgTo( u , msg )
            
    def getRootRoom(self) :
        if( self.parentRoom!=None ) :
            return self.parentRoom.getRootRoom()
        return self;
    
    def getRoomFromPath(self, path) : 
        if path==''  :
            return self
        
        p = path.split('/')
        
        if p[0]=='' : # "/blah/blah"
            return self.getRootRoom().getRoomFromPath( '/'.join(p[1:])) 
            
        if p[0]=='.' : # "./blah/blah"
            return self.getRoomFromPath('/'.join(p[1:])) 
            
        if p[0]=='..' : # "../blah/blah"
            if self.parentRoom!=None : 
                return self.parentRoom.getRoomFromPath('/'.join(p[1:])) 
            else :
                return None
                
        if self.subRooms.has_key(p[0]) : # "blah/blah"
            return self.subRooms[p[0]].getRoomFromPath( '/'.join(p[1:]) )
        else :
            return None        

    def loadPlugin(self, pluginName) :
        # find module file from specific directory
        try :
            file, filename, description = imp.find_module( pluginName, [Config.get("blink.plugin_path")])
            # load module
            pluginmodule = imp.load_module(pluginName, file, filename, description)
            pluginClass = getattr(pluginmodule, pluginName)
            self.plugins[pluginName] =  pluginClass(self) 
            self.events.dispatch('loadplugin', Event.Event( {'plugin': pluginName} ) )
            dbg("load plugin ok: "+pluginName)
        except Exception ,e: 
            err("load plugin failed : "+pluginName+", "+str(e))


